using System.Text.Json;
using Newtonsoft.Json.Linq;

namespace QiaoAiGRPCService.Helper
{
    public record ConfigErrorMsg(string Path, Exception Exception);

    public class EnvironmentJsonHelper
    {
        private readonly string _configPath;
        private readonly object _fileLock = new();
        private JObject _rootNode;
        private readonly IConfigurationRoot _configRoot;

        public static EnvironmentJsonHelper Instance { get; } = new();

        public event Action<ConfigErrorMsg>? ReadConfigError;
        public event Action<ConfigErrorMsg>? WriteConfigError;

        private EnvironmentJsonHelper()
        {
            _configPath = Path.Combine(AppContext.BaseDirectory, "EnvironmentConfig.json");
            Console.WriteLine($"Config path: {_configPath}");

            lock (_fileLock)
            {
                InitializeConfigFile();
                _rootNode = LoadConfigFile();
            }

            _configRoot = new ConfigurationBuilder()
                .AddJsonFile(_configPath, optional: true)
                .Build();
        }

        private void InitializeConfigFile()
        {
            if (File.Exists(_configPath))
                return;

            var initialContent = new { };
            File.WriteAllText(
                _configPath,
                JsonSerializer.Serialize(
                    initialContent,
                    new JsonSerializerOptions { WriteIndented = true }
                )
            );
        }

        private JObject LoadConfigFile()
        {
            try
            {
                return JObject.Parse(File.ReadAllText(_configPath));
            }
            catch
            {
                var newConfig = new JObject();
                File.WriteAllText(_configPath, newConfig.ToString());
                return newConfig;
            }
        }

        public bool TryGetConfig<T>(string path, out T value, T defaultValue = default)
        {
            value = defaultValue;

            try
            {
                var token = GetTokenByPath(path);
                if (token == null)
                    return false;
                var result = token.ToObject<T>();
                if (result == null)
                    value = defaultValue;
                else
                    value = result;
                return true;
            }
            catch (Exception ex)
            {
                ReadConfigError?.Invoke(new ConfigErrorMsg(path, ex));
                return false;
            }
        }

        public T? GetConfig<T>(string path)
        {
            return TryGetConfig(path, out T? value) ? value : default;
        }

        public bool SetConfig(string path, JToken value)
        {
            try
            {
                lock (_fileLock)
                {
                    var keys = path.Split(':');
                    var current = _rootNode;

                    for (var i = 0; i < keys.Length - 1; i++)
                    {
                        var key = keys[i];
                        if (current[key] is not JObject next)
                        {
                            next = new JObject();
                            current[key] = next;
                        }
                        current = next;
                    }

                    var finalKey = keys[^1];
                    current[finalKey] = value;

                    File.WriteAllText(_configPath, _rootNode.ToString());
                    return true;
                }
            }
            catch (Exception ex)
            {
                WriteConfigError?.Invoke(new ConfigErrorMsg(path, ex));
                return false;
            }
        }

        private JToken? GetTokenByPath(string path)
        {
            var current = (JToken?)_rootNode;
            foreach (var key in path.Split(':'))
            {
                current = current?[key];
                if (current == null)
                    break;
            }
            return current;
        }
    }
}
